package com.guimin.common.util;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.*;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.*;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.*;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

public class HttpClients {
	
	private static Logger logger = LoggerFactory.getLogger(HttpClients.class);

    /**
     * 普通http
     */
    public final static String HTTP_TYPE_COMMON = "1";
    /**
     * https无校验
     */
    public final static String HTTP_TYPE_AUTH_NONE = "2";
    /**
     * http单向认证
     */
    public final static String HTTP_TYPE_AUTH_SINGLE = "3";
    /**
     * http双向认证
     */
    public final static String HTTP_TYPE_AUTH_TWO = "4";

    /**
     * 获取httpClient
     *
     * @param httpType
     * @return
     */
    public static HttpClient createHttpClient(String httpType) {
        if (HttpClients.HTTP_TYPE_COMMON.equals(httpType)) {
            return org.apache.http.impl.client.HttpClients.createDefault();
        } else if (HttpClients.HTTP_TYPE_AUTH_NONE.equals(httpType)) {
            return createAuthNonHttpClient();
        } else if (HttpClients.HTTP_TYPE_AUTH_SINGLE.equals(httpType)) {
            throw new UnsupportedOperationException("");
        } else if (HttpClients.HTTP_TYPE_AUTH_TWO.equals(httpType)) {
            throw new UnsupportedOperationException("");
        }
        throw new UnsupportedOperationException("");
    }

    public static HttpClient createAuthNonHttpClient() {
        SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(100000).build();
        RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.<ConnectionSocketFactory>create();
        ConnectionSocketFactory plainSF = new PlainConnectionSocketFactory();
        registryBuilder.register("http", plainSF);
        //指定信任密钥存储对象和连接套接字工厂
        try {
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            SSLContext sslContext = SSLContexts.custom().useTLS().loadTrustMaterial(trustStore, new AnyTrustStrategy()).build();
            LayeredConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            registryBuilder.register("https", sslSF);
        } catch (KeyStoreException e) {
            throw new RuntimeException(e);
        } catch (KeyManagementException e) {
            throw new RuntimeException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        Registry<ConnectionSocketFactory> registry = registryBuilder.build();
        //设置连接参数
        ConnectionConfig connConfig = ConnectionConfig.custom().setCharset(Charset.forName("utf-8")).build();
        //设置连接管理器
        PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(registry);
        connManager.setDefaultConnectionConfig(connConfig);
        connManager.setDefaultSocketConfig(socketConfig);
        //指定cookie存储对象
        BasicCookieStore cookieStore = new BasicCookieStore();
        return HttpClientBuilder.create().setDefaultCookieStore(cookieStore).setConnectionManager(connManager).build();
    }

    private static class AnyTrustStrategy implements TrustStrategy {

        @Override
        public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            return true;
        }

    }
    
    /**
     * 发送json格式请求到指定地址
     * @param url
     * @param json
     * @return
     */
    public static String sendRequest(String url, String json) {
        int timeout=60000;                                     //超时时间
        long responseLength = 0;                         //响应长度
        String responseContent = null;                 //响应内容
        String strResult = "";
        HttpClient httpClient = new DefaultHttpClient();
        wrapClient(httpClient);
        try {
            HttpParams httpParams = httpClient.getParams();
            httpParams.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, timeout);
            httpParams.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, timeout);
            HttpPost httpPost = new HttpPost(url);                        //创建HttpPost
            StringEntity se = new StringEntity(json, "UTF-8");
            se.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
            se.setContentEncoding("UTF-8");
            httpPost.setEntity(se);
            HttpResponse response = httpClient.execute(httpPost); //执行POST请求

            // 若状态码为200 ok
            if (response.getStatusLine().getStatusCode() == 200) {
                // 取出回应字串
                strResult = EntityUtils.toString(response.getEntity());
            }
//            System.out.println("请求地址: " + httpPost.getURI());
//            System.out.println("响应状态: " + response.getStatusLine());
//            System.out.println("响应长度: " + responseLength);
//            System.out.println("响应内容: " + responseContent);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (httpClient != null) {
                httpClient.getConnectionManager().shutdown();//关闭连接,释放资源
            }
        }
        return strResult;
    }

    public static String sendRequest(String url, Map<String, String> data) {
        int timeout=15000;                                     //超时时间
        long responseLength = 0;                         //响应长度
        String responseContent = null;                 //响应内容
        String strResult = "";
        HttpClient httpClient = new DefaultHttpClient();
        wrapClient(httpClient);
        try {
            HttpParams httpParams = httpClient.getParams();
            httpParams.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, timeout);
            httpParams.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, timeout);
            httpParams.setParameter(HTTP.DEFAULT_PROTOCOL_CHARSET, "UTF-8");
            
            HttpPost httpPost = new HttpPost(url);//创建HttpPost
            
            List<NameValuePair> parameters = new ArrayList<NameValuePair>();
            Iterator it = data.entrySet().iterator();
            while(it.hasNext()){  
            	Entry entry = (Entry)it.next();
            	parameters.add(new BasicNameValuePair((String)entry.getKey(), (String)entry.getValue()));
            }            
              
            // 创建UrlEncodedFormEntity对象  
            UrlEncodedFormEntity formEntiry = new UrlEncodedFormEntity(parameters,"UTF-8");
            httpPost.setEntity(formEntiry);  
            
            HttpResponse response = httpClient.execute(httpPost); //执行POST请求

            // 若状态码为200 ok
            if (response.getStatusLine().getStatusCode() == 200) {
            	String charset = getCharset(response.getEntity());
            	if("UTF-8".equals(charset)){
            		strResult =  EntityUtils.toString(response.getEntity());
            	}else{
            		strResult =  new String((EntityUtils.toString(response.getEntity())).getBytes("ISO-8859-1"),"UTF-8");
            	}
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (httpClient != null)
                httpClient.getConnectionManager().shutdown();//关闭连接,释放资源
        }
        return strResult;
    }
    
    /**
     * https 不验证证书
     * @param httpClient
     */
    public static void wrapClient(HttpClient httpClient) {
        try {
            X509TrustManager xtm = new X509TrustManager() {   //创建TrustManager
                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                }
                public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                }
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };
            //TLS1.0与SSL3.0基本上没有太大的差别，可粗略理解为TLS是SSL的继承者，但它们使用的是相同的SSLContext
            SSLContext ctx = SSLContext.getInstance("TLS");
            //使用TrustManager来初始化该上下文，TrustManager只是被SSL的Socket所使用
            ctx.init(null, new TrustManager[]{xtm}, null);
            //创建SSLSocketFactory
            SSLSocketFactory socketFactory = new SSLSocketFactory(ctx);
            //通过SchemeRegistry将SSLSocketFactory注册到我们的HttpClient上
            httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, socketFactory));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    
    public static String getCharset(HttpEntity entity){
    	Header header = entity.getContentType();
    	if(header!=null){
    		HeaderElement[] hes = header.getElements();
    		for(HeaderElement he : hes){
    			for(NameValuePair p : he.getParameters()){
    				if("charset".equals(p.getName())){
    					return p.getValue();
    				}
    			}
    		}
    	}
		return "";
		
    }
    
    
    
    public static CloseableHttpClient createHttpClientWithCert(KeyStore keyStore, String keyStorePassword, KeyStore trustStoreFile,
                                                               int connMaxTotal, int connDefaultMaxPerRoute, int validateInactivityMillSeconds, int connEvictIdleConnectionsTimeoutMillSeconds,
                                                               String proxyHost, int proxyPort, String proxyUsername, String proxyPassword) {
		SSLContext sslcontext = null;
		try {
			sslcontext = SSLContexts.custom()
			.loadKeyMaterial(keyStore, keyStorePassword.toCharArray())
			.loadTrustMaterial(trustStoreFile, new TrustSelfSignedStrategy()).build();
		} catch (Exception e) {
			throw new RuntimeException("key store fail", e);
		}
		
		HostnameVerifier allHostsValid = new HostnameVerifier() {
			public boolean verify(String hostname, SSLSession session) {
				return true;
			}
		};

		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,new String[]{"TLSv1","TLSv1.1","TLSv1.2"},null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
		Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
				.register("https", sslsf)
				.register("http", PlainConnectionSocketFactory.INSTANCE)
				.build();
			
		PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
		SocketConfig socketConfig = SocketConfig.custom().setTcpNoDelay(true).build();
		connManager.setDefaultSocketConfig(socketConfig);
		connManager.setValidateAfterInactivity(validateInactivityMillSeconds);
		connManager.setMaxTotal(connMaxTotal);
		connManager.setDefaultMaxPerRoute(connDefaultMaxPerRoute);
		
		CookieStore cookieStore = new BasicCookieStore();
		RequestConfig defaultRequestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.DEFAULT).setExpectContinueEnabled(true).build();
		
		HttpHost proxy = null;
		if (StringUtils.isNotEmpty(proxyHost)) {
			proxy = new HttpHost(proxyHost, proxyPort, "http");
		}
		CredentialsProvider credsProvider = new BasicCredentialsProvider();
		if (StringUtils.isNotEmpty(proxyUsername) && StringUtils.isNotEmpty(proxyPassword)) {
			credsProvider.setCredentials(
			new AuthScope(proxyHost, proxyPort),
			new UsernamePasswordCredentials(proxyUsername, proxyPassword));
		}
		
		CloseableHttpClient httpclient;
		if (proxy == null) {
			httpclient = org.apache.http.impl.client.HttpClients.custom().setConnectionManager(connManager)
					.setDefaultCookieStore(cookieStore)
					.setDefaultRequestConfig(defaultRequestConfig).evictExpiredConnections()
					.evictIdleConnections(connEvictIdleConnectionsTimeoutMillSeconds, TimeUnit.MILLISECONDS)
					.setSSLSocketFactory(sslsf).build();
		} else {
			httpclient = org.apache.http.impl.client.HttpClients.custom().setConnectionManager(connManager)
					.setProxy(proxy)
					.setDefaultCredentialsProvider(credsProvider)
					.setDefaultCookieStore(cookieStore)
					.setDefaultRequestConfig(defaultRequestConfig).evictExpiredConnections()
					.evictIdleConnections(connEvictIdleConnectionsTimeoutMillSeconds, TimeUnit.MILLISECONDS)
					.setSSLSocketFactory(sslsf).build();
		}
		return httpclient;
	}
    
}


